
// <copyright file="SimpleTimedCache.cs" company="Subhadra Software Solutions">
//   
// </copyright>
// <summary>
//   The get cache object callback.
// </summary>

namespace SubhadraSolutions.Sharp.Utils.Collections.Generic
{
    using System;
    using System.Threading;

    #region Delegates

    /// <summary>
    /// The get cache object callback.
    /// </summary>
    /// <typeparam name="T">
    /// </typeparam>
    public delegate T GetCacheObjectCallback<out T>();

    #endregion Delegates

    /// <summary>
    /// The simple timed cache.
    /// </summary>
    /// <typeparam name="T">
    /// </typeparam>
    public sealed class SimpleTimedCache<T> : AbstractDisposable
    {
        #region Fields

        /// <summary>
        /// The callback.
        /// </summary>
        private readonly GetCacheObjectCallback<T> callback;

        /// <summary>
        /// The lock.
        /// </summary>
        private readonly ReaderWriterLockSlim readerWriterLockSlim = new ReaderWriterLockSlim();

        /// <summary>
        /// The cache object.
        /// </summary>
        private T cacheObject;

        /// <summary>
        /// The last creation time.
        /// </summary>
        private DateTime? lastCreationTime;

        #endregion Fields

        #region Constructors and Finalizers

        /// <summary>
        /// Initializes a new instance of the <see cref="SimpleTimedCache{T}"/> class.
        /// </summary>
        /// <param name="interval">
        /// The interval.
        /// </param>
        /// <param name="callback">
        /// The callback.
        /// </param>
        public SimpleTimedCache(long interval, GetCacheObjectCallback<T> callback)
        {
            this.Interval = interval;
            this.callback = callback;
        }

        #endregion Constructors and Finalizers

        #region Public Properties

        /// <summary>
        /// Gets the interval.
        /// </summary>
        public long Interval { get; private set; }

        #endregion Public Properties

        #region Public Methods and Operators

        /// <summary>
        /// The get object.
        /// </summary>
        /// <returns>
        /// The <see cref="T"/>.
        /// </returns>
        public T GetObject()
        {
            this.readerWriterLockSlim.EnterUpgradeableReadLock();
            try
            {
                if (this.ShouldRefreshCache())
                {
                    this.readerWriterLockSlim.EnterWriteLock();
                    try
                    {
                        if (this.ShouldRefreshCache())
                        {
                            this.refreshCache();
                        }
                    }
                    finally
                    {
                        this.readerWriterLockSlim.ExitWriteLock();
                    }
                }
            }
            finally
            {
                this.readerWriterLockSlim.ExitUpgradeableReadLock();
            }

            return this.cacheObject;
        }

        #endregion Public Methods and Operators

        #region Methods

        /// <summary>
        /// The dispose.
        /// </summary>
        /// <param name="disposing">
        /// The disposing.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            this.readerWriterLockSlim.Dispose();
        }

        /// <summary>
        /// The refresh cache.
        /// </summary>
        private void refreshCache()
        {
            this.cacheObject = this.callback();
            this.lastCreationTime = GlobalSettings.Instance.CurrentDateTime;
        }

        /// <summary>
        /// The should refresh cache.
        /// </summary>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        private bool ShouldRefreshCache()
        {
            if (this.lastCreationTime == null)
            {
                return true;
            }

            if ((GlobalSettings.Instance.CurrentDateTime - this.lastCreationTime.Value).TotalMilliseconds >= this.Interval)
            {
                return true;
            }

            return false;
        }

        #endregion Methods
    }
}